﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.IO;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using Bymed.SpineScan.WPF.Shared;
using Bymed.SpineScan.WPF.Model;
using Bymed.SpineScan.Service;
using System.Security;
using System.Text;
using System.Windows;
using Bymed.SpineScan.Service.Shared;
using Bymed.SpineScan.DataModel;
using Bymed.SpineScan.Service.Interface;
using System.Collections;
using System.Collections.Generic;

namespace Bymed.SpineScan.WPF.ViewModel
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class DataManagementViewModel : MyViewModelBase
    {
        public const string RequestDisplayOpenDataSourceFileDialogMessageToken = "DataManagementRequestDisplayOpenDataSourceFileDialogMessage";
        public const string RequestDisplayOpenPatientDataFileDialogMessageToken = "DataManagementRequestDisplayOpenPatientDataFileDialogMessage";
        public const string RequestDisplaySaveExportTestReportFileDialogMessageToken = "DataManagementRequestDisplaySaveExportTestReportFileDialogMessage";
        public const string CleanDataSourceConfirmMessageToken = "DataManagementCleanDataSourceConfirmMessage";
        protected string ExportDataSourceFileFullName;
        protected string ExportPatientDataXmlFileFullName;

        /// <summary>
        /// Initializes a new instance of the PatientDataSearchViewModel class.
        /// </summary>
        public DataManagementViewModel(IDataService dataService)
            : base(dataService)
        {
            IsSqlServerCe = ConfigFileUtility.GetDataSourceType(ConfigurationConstant.ConnectionName) == DataSourceType.SqlServerCe;
        }

        protected override void RegisterMessages()
        {
            base.RegisterMessages();

            RegisterSelectedDataSourceFileMessage();
            RegisterSelectedPatientDataFileMessage();
            RegisterSelectedExportTestReportFileMessage();
        }

        public override void ResetProperties()
        {
            base.ResetProperties();

            ImportedDataSourceFile = false;
            ExportDataSourceFileName = string.Empty;
            ImportedPatientDataFile = false;
            ExportPatientDataFileName = string.Empty;
            CleanedDataSource = false;
        }

        /// <summary>
        /// The <see cref="IsSqlServerCe" /> property's name.
        /// </summary>
        public const string IsSqlServerCePropertyName = "IsSqlServerCe";

        private bool _isSqlServerCe = false;

        /// <summary>
        /// Sets and gets the IsSqlServerCe property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsSqlServerCe
        {
            get
            {
                return _isSqlServerCe;
            }

            set
            {
                if (_isSqlServerCe == value)
                {
                    return;
                }

                RaisePropertyChanging(IsSqlServerCePropertyName);
                _isSqlServerCe = value;
                RaisePropertyChanged(IsSqlServerCePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="ImportedDataSourceFile" /> property's name.
        /// </summary>
        public const string ImportedDataSourceFilePropertyName = "ImportedDataSourceFile";

        private bool _importedDataSourceFile = false;

        /// <summary>
        /// Sets and gets the ImportedDataSourceFile property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool ImportedDataSourceFile
        {
            get
            {
                return _importedDataSourceFile;
            }

            set
            {
                if (_importedDataSourceFile == value)
                {
                    return;
                }

                RaisePropertyChanging(ImportedDataSourceFilePropertyName);
                _importedDataSourceFile = value;
                RaisePropertyChanged(ImportedDataSourceFilePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="ExportDataSourceFileName" /> property's name.
        /// </summary>
        public const string ExportDataSourceFileNamePropertyName = "ExportDataSourceFileName";

        private string _exportDataSourceFileName = string.Empty;

        /// <summary>
        /// Sets and gets the ExportDataSourceFileName property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string ExportDataSourceFileName
        {
            get
            {
                return _exportDataSourceFileName;
            }

            set
            {
                if (_exportDataSourceFileName == value)
                {
                    return;
                }

                RaisePropertyChanging(ExportDataSourceFileNamePropertyName);
                _exportDataSourceFileName = value;
                RaisePropertyChanged(ExportDataSourceFileNamePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="ExportPatientDataFileName" /> property's name.
        /// </summary>
        public const string ExportPatientDataFileNamePropertyName = "ExportPatientDataFileName";

        private string _exportPatientDataFileName = string.Empty;

        /// <summary>
        /// Sets and gets the ExportPatientDataFileName property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string ExportPatientDataFileName
        {
            get
            {
                return _exportPatientDataFileName;
            }

            set
            {
                if (_exportPatientDataFileName == value)
                {
                    return;
                }

                RaisePropertyChanging(ExportPatientDataFileNamePropertyName);
                _exportPatientDataFileName = value;
                RaisePropertyChanged(ExportPatientDataFileNamePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="ImportedPatientDataFile" /> property's name.
        /// </summary>
        public const string ImportedPatientDataFilePropertyName = "ImportedPatientDataFile";

        private bool _importedPatientDataFile = false;

        /// <summary>
        /// Sets and gets the ImportedPatientDataFile property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool ImportedPatientDataFile
        {
            get
            {
                return _importedPatientDataFile;
            }

            set
            {
                if (_importedPatientDataFile == value)
                {
                    return;
                }

                RaisePropertyChanging(ImportedPatientDataFilePropertyName);
                _importedPatientDataFile = value;
                RaisePropertyChanged(ImportedPatientDataFilePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="CleanedDataSource" /> property's name.
        /// </summary>
        public const string CleanedDataSourcePropertyName = "CleanedDataSource";

        private bool _cleanedDataSource = false;

        /// <summary>
        /// Sets and gets the CleanedDataSource property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool CleanedDataSource
        {
            get
            {
                return _cleanedDataSource;
            }

            set
            {
                if (_cleanedDataSource == value)
                {
                    return;
                }

                RaisePropertyChanging(CleanedDataSourcePropertyName);
                _cleanedDataSource = value;
                RaisePropertyChanged(CleanedDataSourcePropertyName);
            }
        }

        private RelayCommand _exportDataSourceCommand;

        /// <summary>
        /// Gets the ExportDataSourceCommand.
        /// </summary>
        public RelayCommand ExportDataSourceCommand
        {
            get
            {
                return _exportDataSourceCommand
                    ?? (_exportDataSourceCommand = new RelayCommand(
                                          () =>
                                          {
                                              try
                                              {
                                                  ExportDataSourceFileFullName = ServiceFactory.CreateDataSourceManagementService().ExportDataSourceFile();
                                                  ExportDataSourceFileName = (new FileInfo(ExportDataSourceFileFullName)).Name;
                                                  ImportedDataSourceFile = false;
                                              }
                                              catch (Exception ex)
                                              {
                                                  ExportDataSourceFileName = string.Empty;
                                                  RequestDisplayErrorMessage(ex.Message);
                                              }
                                          },
                                          () => IsSqlServerCe));
            }
        }

        private RelayCommand _importDataSourceCommand;

        /// <summary>
        /// Gets the ImportDataSourceCommand.
        /// </summary>
        public RelayCommand ImportDataSourceCommand
        {
            get
            {
                return _importDataSourceCommand
                    ?? (_importDataSourceCommand = new RelayCommand(
                                          () =>
                                          {
                                              // Send RequestDisplayOpenDataSourceFileDialogMessage to DataManagementPage
                                              Messenger.Default.Send<int>(0, RequestDisplayOpenDataSourceFileDialogMessageToken);
                                          },
                                          () => IsSqlServerCe));
            }
        }

        private RelayCommand _openDataSourceFileExportDirectoryCommand;

        /// <summary>
        /// Gets the OpenDataSourceFileExportDirectoryCommand.
        /// </summary>
        public RelayCommand OpenDataSourceFileExportDirectoryCommand
        {
            get
            {
                return _openDataSourceFileExportDirectoryCommand
                    ?? (_openDataSourceFileExportDirectoryCommand = new RelayCommand(
                                          () =>
                                          {
                                              Process.Start(Directory.GetParent(ExportDataSourceFileFullName).FullName);
                                          }));
            }
        }

        private RelayCommand _exportPatientDataCommand;

        /// <summary>
        /// Gets the ExportPatientDataCommand.
        /// </summary>
        public RelayCommand ExportPatientDataCommand
        {
            get
            {
                return _exportPatientDataCommand
                    ?? (_exportPatientDataCommand = new RelayCommand(
                                          () =>
                                          {
                                              try
                                              {
                                                  ExportPatientDataXmlFileFullName = ServiceFactory.CreateDataSourceManagementService().ExportDataSourceToXMLFile();
                                                  ExportPatientDataFileName = ((new FileInfo(ExportPatientDataXmlFileFullName))).Name;
                                                  ImportedPatientDataFile = false;
                                              }
                                              catch (Exception ex)
                                              {
                                                  RequestDisplayErrorMessage(ex.Message);
                                              }
                                          }));
            }
        }

        private RelayCommand _importPatientDataCommand;

        /// <summary>
        /// Gets the ImportPatientDataCommand.
        /// </summary>
        public RelayCommand ImportPatientDataCommand
        {
            get
            {
                return _importPatientDataCommand
                    ?? (_importPatientDataCommand = new RelayCommand(
                                          () =>
                                          {
                                              // Send RequestDisplayOpenPatientDataFileDialogMessage to DataManagementPage
                                              Messenger.Default.Send<int>(0, RequestDisplayOpenPatientDataFileDialogMessageToken);
                                          }));
            }
        }

        private RelayCommand _openPatientDataXmlFileExportDirectoryCommand;

        /// <summary>
        /// Gets the OpenPatientDataXmlFileExportDirectoryCommand.
        /// </summary>
        public RelayCommand OpenPatientDataXmlFileExportDirectoryCommand
        {
            get
            {
                return _openPatientDataXmlFileExportDirectoryCommand
                    ?? (_openPatientDataXmlFileExportDirectoryCommand = new RelayCommand(
                                          () =>
                                          {
                                              Process.Start(Directory.GetParent(ExportPatientDataXmlFileFullName).FullName);
                                          }));
            }
        }

        private RelayCommand _exportTestReportCommand;

        /// <summary>
        /// Gets the ExportTestReportCommand.
        /// </summary>
        public RelayCommand ExportTestReportCommand
        {
            get
            {
                return _exportTestReportCommand
                    ?? (_exportTestReportCommand = new RelayCommand(
                                          () =>
                                          {
                                              // Send RequestDisplaySaveExportTestReportFileDialogMessage to DataManagementPage
                                              Messenger.Default.Send<int>(0, RequestDisplaySaveExportTestReportFileDialogMessageToken);
                                          }));
            }
        }

        private RelayCommand<string> _cleanDataSourceCommand;

        /// <summary>
        /// Gets the CleanDataSourceCommand.
        /// </summary>
        public RelayCommand<string> CleanDataSourceCommand
        {
            get
            {
                return _cleanDataSourceCommand
                    ?? (_cleanDataSourceCommand = new RelayCommand<string>(
                                          p =>
                                          {
                                              try
                                              {
                                                  // Show a message to confirm whether user needs to clean data source.
                                                  var dialogMessage = new DialogMessage("确认清除当前数据库所有数据？该操作将不可恢复！", dialogResult =>
                                                      {
                                                          // This is the CallBack of the DialogMessage.
                                                          if (dialogResult == MessageBoxResult.Yes)
                                                          {
                                                              try
                                                              {
                                                                  ServiceFactory.CreateDataSourceManagementService().CleanDataSource();
                                                                  CleanedDataSource = true;
                                                              }
                                                              catch (Exception ex)
                                                              {
                                                                  RequestDisplayErrorMessage(ex.Message);
                                                              }
                                                          }
                                                      })
                                                  {
                                                      Button = MessageBoxButton.YesNo,
                                                      Caption = "清除数据库",
                                                      Icon = MessageBoxImage.Question
                                                  };

                                                  Messenger.Default.Send<DialogMessage>(dialogMessage, CleanDataSourceConfirmMessageToken);
                                              }
                                              catch (Exception ex)
                                              {
                                                  RequestDisplayErrorMessage(ex.Message);
                                              }
                                          },
                                          p => p == "123"));
            }
        }

        /// <summary>
        /// Register SelectedDataSourceFileMessage from DataManagementPage.
        /// </summary>
        private void RegisterSelectedDataSourceFileMessage()
        {
            Messenger.Default.Register<string>(this, DataManagementPage.SelectedDataSourceFileMessageToken, p =>
            {
                try
                {
                    ExportDataSourceFileFullName = ServiceFactory.CreateDataSourceManagementService().ImportDataSourceFile(p);
                    ExportDataSourceFileName = (new FileInfo(ExportDataSourceFileFullName)).Name;
                    ImportedDataSourceFile = true;
                }
                catch (Exception ex)
                {
                    ExportDataSourceFileName = string.Empty;
                    RequestDisplayErrorMessage(ex.Message);
                }
            });
        }

        /// <summary>
        /// Register SelectedPatientDataFileMessage from DataManagementPage.
        /// </summary>
        private void RegisterSelectedPatientDataFileMessage()
        {
            Messenger.Default.Register<string>(this, DataManagementPage.SelectedPatientDataFileMessageToken, p =>
            {
                try
                {
                    ServiceFactory.CreateDataSourceManagementService().ImportXMLFileToDataSource(p);
                    ImportedPatientDataFile = true;
                    ExportPatientDataFileName = string.Empty;
                }
                catch (Exception ex)
                {
                    RequestDisplayErrorMessage(ex.Message);
                }
            });
        }

        /// <summary>
        /// Register SelectedExportTestReportFileMessage from DataManagementPage.
        /// </summary>
        private void RegisterSelectedExportTestReportFileMessage()
        {
            Messenger.Default.Register<string>(this, DataManagementPage.SelectedExportTestReportFileMessageToken, p =>
            {
                try
                {
                    ExportTestReport(p);
                }
                catch (Exception ex)
                {
                    RequestDisplayErrorMessage(ex.Message);
                }
            });
        }

        /// <summary>
        /// Export the patient & test result to csv file.
        /// </summary>
        /// <param name="exportTestReportFullName">The export test report csv file full name.</param>
        private void ExportTestReport(string exportTestReportFullName)
        {
            var customizedTestItemService = ServiceFactory.CreateCustomizedTestItemService();
            var allCustomizedTestItems = customizedTestItemService.GetAll().OrderBy(p => p.OrderIndex);

            using (var fs = new FileStream(exportTestReportFullName, FileMode.Create, FileAccess.Write))
            {
                using (var sw = new StreamWriter(fs, Encoding.Unicode))
                {
                    OutputExportReportHeader(sw, allCustomizedTestItems);
                    var patients = ServiceFactory.CreatePatientService().GetAllWithTestResults();

                    foreach (var patient in patients)
                    {
                        foreach (var testTracking in patient.TestTrackings)
                        {
                            OutputTestResult(sw, patient, testTracking, allCustomizedTestItems);
                        }
                    }
                }
            }

            Process.Start(exportTestReportFullName);
        }

        /// <summary>
        /// Output the report header
        /// </summary>
        /// <param name="sw">An instance of StreamWriter.</param>
        /// <param name="allCustomizedTestItems">All CustomizedTestItems based on OrderIndex.</param>
        private void OutputExportReportHeader(StreamWriter sw, IEnumerable<CustomizedTestItem> allCustomizedTestItems)
        {
            var header = "\"编号\",\"姓名\",\"性别\",\"生日\",\"身高(厘米)\",\"体重(公斤)\",\"备注\",\"主治医生\",\"测试日期\"," +
                "\"躯干倾斜角ATI\",\"脊柱后凸\",\"颈椎侧向弯曲\",\"胸椎侧向弯曲\",\"腰椎侧向弯曲\",\"颈椎前屈后伸\",\"胸椎前屈后伸\"," +
                "\"腰椎前屈后伸\",\"颈椎水平旋转\",\"胸椎水平旋转\",\"腰椎水平旋转\",\"头部平衡\",\"肩部平衡\",\"髋骨平衡\"";

            foreach (var item in allCustomizedTestItems)
            {
                header += string.Format(",\"{0}\"", item.ItemTitle);
            }

            sw.WriteLine(header);
        }

        /// <summary>
        /// Output the test result of one patient.
        /// </summary>
        /// <param name="sw">An instance of StreamWriter.</param>
        /// <param name="patient">The instance of Patient.</param>
        /// <param name="testTracking">The Patient's TestTracking.</param>
        /// <param name="allCustomizedTestItems">All CustomizedTestItems based on OrderIndex.</param>
        private void OutputTestResult(StreamWriter sw, Patient patient, TestTracking testTracking, IEnumerable<CustomizedTestItem> allCustomizedTestItems)
        {
            var sb = new StringBuilder();

            sb.AppendFormat("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\",\"{8}\",",
                patient.Number, patient.Name, GeneralUtility.GetGenderDescription(patient.Gender),
                patient.Birthday.HasValue ? patient.Birthday.Value.ToString(GeneralUtility.ShortDateFormat) : string.Empty,
                patient.Height, patient.Weight, patient.Remark, testTracking.AttendingDoctorName, testTracking.TestDate);

            var allTestResultsViewModel = new AllTestResultsViewModel(null);
            ViewModelHelper.InitializeAllTestProperties(allTestResultsViewModel, testTracking);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.ScoliosisATI == null ?
                string.Empty : allTestResultsViewModel.ScoliosisATI.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.Kyphosis == null ?
                string.Empty : allTestResultsViewModel.Kyphosis.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.ThoracicLateralBending == null ?
                string.Empty : allTestResultsViewModel.ThoracicLateralBending.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.CervicalLateralBending == null ?
                string.Empty : allTestResultsViewModel.CervicalLateralBending.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.LumbarLateralBending == null ?
                string.Empty : allTestResultsViewModel.LumbarLateralBending.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.ThoracicFlexExt == null ?
                string.Empty : allTestResultsViewModel.ThoracicFlexExt.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.CervicalFlexExt == null ?
                string.Empty : allTestResultsViewModel.CervicalFlexExt.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.LumbarFlexExt == null ?
                string.Empty : allTestResultsViewModel.LumbarFlexExt.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.ThoracicRotation == null ?
                string.Empty : allTestResultsViewModel.ThoracicRotation.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.CervicalRotation == null ?
                string.Empty : allTestResultsViewModel.CervicalRotation.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.LumbarRotation == null ?
                string.Empty : allTestResultsViewModel.LumbarRotation.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.HeadBalance == null ?
                string.Empty : allTestResultsViewModel.HeadBalance.FormattedTestResult);

            sb.AppendFormat("\"{0}\",", allTestResultsViewModel.ShouldersBalance == null ?
                string.Empty : allTestResultsViewModel.ShouldersBalance.FormattedTestResult);

            sb.AppendFormat("\"{0}\"", allTestResultsViewModel.PelvicBalance == null ?
                string.Empty : allTestResultsViewModel.PelvicBalance.FormattedTestResult);

            // Get all CustomizedTestItem test results.
            foreach (var item in allCustomizedTestItems)
            {
                var matchedResult = testTracking.TestResults.FirstOrDefault(p => p.TestItemName == item.ItemTimestamp);

                if (matchedResult == null)
                {
                    sb.Append(",\"\"");
                }
                else
                {
                    var matchedItem = ViewModelHelper.ConvertFromTestResult(matchedResult,
                        ViewModelHelper.CreateCustomizedTestItemViewModel(item));

                    sb.AppendFormat(",\"{0}\"", matchedItem.FormattedTestResult);
                }
            }

            sw.WriteLine(sb.ToString());
        }
    }
}